#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// using namespace std;

std::string default_svr_ip = "127.0.0.1";
uint16_t default_svr_port = 8080;

int default_sockfd = -1;
int default_retry_interval = 1;
int default_max_retry_attempts = 1;

enum ExitCode
{
    USAGE = 1,
    SOCKET,
};

enum class Status
{
    NEW,
    CONNECTED,
    CONNECTING,
    DISCONNECTED,
    CLOSED,
};

class ClientConnect
{
public:
    ClientConnect(std::string svr_ip = default_svr_ip, uint16_t svr_port = default_svr_port)
        : _sockfd(default_sockfd), _svr_ip(svr_ip), _svr_port(default_svr_port), _retry_interval(default_retry_interval), _max_retry_attempts(default_max_retry_attempts), _stat(Status::NEW)
    {
    }

    Status Stat() { return _stat; }

    void Connect()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "socket create error! " << std::endl;
            exit(ExitCode::SOCKET);
        }
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_svr_port);
        inet_pton(AF_INET, _svr_ip.c_str(), &server.sin_addr);

        int n = connect(_sockfd, (struct sockaddr*)&server, sizeof(server));
        if(n < 0){
            // 关闭 sockfd
            Disconnect();
            // 更新
            _stat = Status::DISCONNECTED;
            return;
        }
        _stat = Status::CONNECTED;
        std::cout << "connect success! " << std::endl;
    }

    void Reconnect() {
        _stat = Status::CONNECTING;
        int cnt = 0;
        while(true){
            Connect();
            if(_stat == Status::CONNECTED) 
            {
                std::cerr << "reconnect success! " << std::endl;
                break;
            }
            cnt++;
            if(cnt > _max_retry_attempts){
                _stat = Status::CLOSED;
                std::cerr << "reconnect fail.. " << std::endl;
                break;
            }
            std::cout << "reconnecting... : " << cnt << std::endl;
            sleep(_retry_interval);
        }

    }

    void Process() // 正常 IO 通信
    {
        while(true)
        {
            std::string msg = "hello, server";
            ssize_t n = send(_sockfd, msg.c_str(), msg.size(), 0);
            if(n > 0){
                char buffer[1024];
                ssize_t m = recv(_sockfd, buffer, sizeof(buffer), 0);
                if(m > 0){
                    buffer[m] = 0;
                    std::cout << "server echo# " << buffer << std::endl;
                }
                else{
                    _stat = Status::DISCONNECTED;
                    break;
                }
            }
            else{
                std::cerr << "send error " << std::endl;
                // _stat = Status::DISCONNECTED;
                _stat = Status::CLOSED;
                break;
            }
            sleep(1);
        }
    }

    void Disconnect() {
        if(_sockfd > default_sockfd){
            ::close(_sockfd);
            _sockfd = default_sockfd;
            // 解耦
            _stat = Status::CLOSED;
        }

    }

    ~ClientConnect() {}

private:
    int _sockfd;
    std::string _svr_ip;
    uint16_t _svr_port;

    int _retry_interval;     // 重连间隔
    int _max_retry_attempts; // 最大重连次数

    Status _stat;
};

class TcpConnect
{
public:
    TcpConnect(ClientConnect &cc)
        : _cc(cc)
    {
    }
    TcpConnect(std::string svr_ip, uint16_t svr_port)
        : _cc(svr_ip, svr_port)
    {
    }

    // 基于ClientConnect的状态，对不同状态执行不同的命令，叫 基于 状态机
    void Execute()
    {
        while (true)
        {
            switch (_cc.Stat())
            {
            case Status::NEW:
            {
                _cc.Connect();
                break;
            }
            case Status::CONNECTED:
            {
                _cc.Process();
                break;
            }
            case Status::DISCONNECTED:
            {
                _cc.Reconnect();
                break;
            }
            case Status::CLOSED:
            {
                return;
            }
            default:
            {
                break;
            }
            }
        }
    }
    ~TcpConnect() {}

private:
    ClientConnect _cc;
};

void Usage(std::string process)
{
    std::cout << process << " serverIp serverPort" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc > 3)
    {
        Usage(argv[0]);
        return ExitCode::USAGE;
    }

    std::string svr_ip = argv[1];
    uint16_t svr_port = std::stoi(argv[2]);
    TcpConnect connect(svr_ip, svr_port);
    connect.Execute();

    return 0;
}
